package com.shenhangye.pushlive.gles

import android.graphics.SurfaceTexture
import android.opengl.EGL14
import android.opengl.EGLConfig
import android.opengl.EGLContext
import android.opengl.EGLDisplay
import android.opengl.EGLExt
import android.opengl.EGLExt.EGL_RECORDABLE_ANDROID
import android.opengl.EGLSurface
import android.util.Log
import android.view.Surface

class EglCore(private var sharedContext:EGLContext? = null, flags:Int) {
    private var mEGLDisplay: EGLDisplay? = EGL14.EGL_NO_DISPLAY
    /**
     * Constructor flag: ask for GLES3, fall back to GLES2 if not available.  Without this
     * flag, GLES2 is used.
     */
    val FLAG_TRY_GLES3: Int = 0x02
    /**
     * Constructor flag: surface must be recordable.  This discourages EGL from using a
     * pixel format that cannot be converted efficiently to something usable by the video
     * encoder.
     */
    private  val TAG = "EglCore"
    companion object{
        @JvmStatic
        public val FLAG_RECORDABLE: Int = 0x01
    }


    private var mEGLContext: EGLContext? = EGL14.EGL_NO_CONTEXT
    private var mEGLConfig: EGLConfig? = null
    private var mGlVersion = -1

    fun release(){
        if (mEGLDisplay!=EGL14.EGL_NO_DISPLAY){

            // Android is unusual in that it uses a reference-counted EGLDisplay.  So for
            // every eglInitialize() we need an eglTerminate().
            EGL14.eglMakeCurrent(
                mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_CONTEXT
            )
            EGL14.eglDestroyContext(mEGLDisplay, mEGLContext)
            EGL14.eglReleaseThread()
            EGL14.eglTerminate(mEGLDisplay)
        }
        mEGLDisplay = EGL14.EGL_NO_DISPLAY
        mEGLContext = EGL14.EGL_NO_CONTEXT
        mEGLConfig = null
    }

    /**
     * Creates an EGL surface associated with a Surface.
     * <p>
     * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
     */
    public fun createWindowSurface(surface:Any):EGLSurface {
        if (surface !is Surface && surface !is SurfaceTexture) {
            throw java.lang.RuntimeException("invalid surface: $surface")
        }

        val surfaceAttribs= intArrayOf(EGL14.EGL_NONE)

        val eglSurface=
            EGL14.eglCreateWindowSurface(mEGLDisplay,mEGLConfig,surface, surfaceAttribs, 0)
                ?: throw java.lang.RuntimeException("surface was null")

        return eglSurface
    }

    /**
     * Creates an EGL surface associated with an offscreen buffer.
     */
    fun createOffScreenSurface(width:Int,height:Int):EGLSurface {

        val surfaceAttribs= intArrayOf(
            EGL14.EGL_WIDTH,width,
            EGL14.EGL_HEIGHT,height,
            EGL14.EGL_NONE)

        val eglSurface=EGL14.eglCreatePbufferSurface(mEGLDisplay,mEGLConfig,surfaceAttribs,0)

        return eglSurface
    }

    /**
     *  Makes our EGL context current, using the supplied surface for both "draw" and "read
     */

    fun makeCurrent(eglSurface:EGLSurface){
        if (mEGLDisplay==EGL14.EGL_NO_DISPLAY){

        }
        if (!EGL14.eglMakeCurrent(mEGLDisplay,eglSurface,eglSurface,mEGLContext)){

        }
    }

    /**
     * Makes our EGL context current, using the supplied "draw" and "read" surfaces.
     */
    fun makeCurrent(drawSurface: EGLSurface?, readSurface: EGLSurface?) {
        if (mEGLDisplay === EGL14.EGL_NO_DISPLAY) {
            // called makeCurrent() before create?
            Log.d(TAG, "NOTE: makeCurrent w/o display")
        }
        if (!EGL14.eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) {
            throw java.lang.RuntimeException("eglMakeCurrent(draw,read) failed")
        }
    }


    /**
     * Makes no context current.
     */
    fun makeNothingCurrent() {
        if (!EGL14.eglMakeCurrent(
                mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_CONTEXT
            )
        ) {
            throw java.lang.RuntimeException("eglMakeCurrent failed")
        }
    }
    /**
     * 把eglSurface上的数据显示到mEGLDisplay上
     */
    fun swapBuffers(eglSurface:EGLSurface):Boolean{
        return EGL14.eglSwapBuffers(mEGLDisplay,eglSurface);
    }

    /**
     * Sends the presentation time stamp to EGL.  Time is expressed in nanoseconds.
     */
    fun setPresentationTime(eglSurface: EGLSurface?, nsecs: Long) {
        EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs)
    }

    fun isCurrent(eglSurface: EGLSurface):Boolean{
        return mEGLContext?.equals(EGL14.eglGetCurrentContext()) == true &&
                eglSurface == EGL14.eglGetCurrentSurface(
            EGL14.EGL_DRAW);
    }

    /**
     * Performs a simple surface query.   返回 what所定义的参数  比如宽度 高度
     */
    fun querySurface(eglSurface: EGLSurface?, what: Int): Int {
        val value = IntArray(1)
        EGL14.eglQuerySurface(mEGLDisplay, eglSurface, what, value, 0)
        return value[0]
    }

    /**
     * Destroys the specified surface.  Note the EGLSurface won't actually be destroyed if it's
     * still current in a context.
     */
    fun releaseSurface(eglSurface: EGLSurface?) {
        EGL14.eglDestroySurface(mEGLDisplay, eglSurface)
    }


    /**
     * Queries a string value.
     */
    fun queryString(what: Int): String {
        return EGL14.eglQueryString(mEGLDisplay, what)
    }

    /**
     * Returns the GLES version this context is configured for (currently 2 or 3).
     */
    fun getGlVersion(): Int {
        return mGlVersion
    }


    /**
     * Writes the current display, context, and surface to the log.
     */
    fun logCurrent(msg: String) {
        val display = EGL14.eglGetCurrentDisplay()
        val context = EGL14.eglGetCurrentContext()
        val surface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW)
        Log.i(
           TAG,
            "Current EGL (" + msg + "): display=" + display + ", context=" + context +
                    ", surface=" + surface
        )
    }

    /**
     * Checks for EGL errors.  Throws an exception if an error has been raised.
     */
    private fun checkEglError(msg: String) {
        var error: Int
        if ((EGL14.eglGetError().also { error = it }) != EGL14.EGL_SUCCESS) {
            throw java.lang.RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error))
        }
    }

    /****
     *@param flag 标志位从构造函数传过来
     *@param version 2 或者3
     */
    fun getConfig(flags:Int,version:Int):EGLConfig?{

        //0x00000040
             //0x0004
        ////// 0x0040
        var renderableType=EGL14.EGL_OPENGL_ES2_BIT;
        if (version>3){
            renderableType = renderableType or EGLExt.EGL_OPENGL_ES3_BIT_KHR
        }

        // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
        // doesn't really help.  It can also lead to a huge performance hit on glReadPixels()
        // when reading into a GL_RGBA buffer.
        val attribList = intArrayOf(
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_ALPHA_SIZE, 8,  //EGL14.EGL_DEPTH_SIZE, 16,
            //EGL14.EGL_STENCIL_SIZE, 8,
            EGL14.EGL_RENDERABLE_TYPE, renderableType,
            EGL14.EGL_NONE, 0,  // placeholder for recordable [@-3]
            EGL14.EGL_NONE
        )

        if ((flags and FLAG_RECORDABLE)!=0){
            attribList[attribList.size - 3] = EGL_RECORDABLE_ANDROID
            attribList[attribList.size - 2] = 1
        }

        val configs = arrayOfNulls<EGLConfig>(1)
        val numConfigs = IntArray(1)
        if (!EGL14.eglChooseConfig(
                mEGLDisplay, attribList, 0, configs, 0, configs.size,
                numConfigs, 0
            )
        ) {
            Log.w(
                TAG,
                "unable to find RGB8888 / $version EGLConfig"
            )
            return null
        }

        return configs[0]
    }
    init{
        if (mEGLDisplay!=EGL14.EGL_NO_DISPLAY){
            throw RuntimeException("EGL already set up")
        }

        if (sharedContext==null) {
            sharedContext = EGL14.EGL_NO_CONTEXT
        }
        //获取默认的display
        mEGLDisplay=EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
        if (mEGLDisplay==EGL14.EGL_NO_DISPLAY){
            throw RuntimeException("unable to get EGL14 display")
        }

        var version=IntArray(2)

        if (!EGL14.eglInitialize(mEGLDisplay,version,0,version,1)){
            mEGLDisplay=null;
            throw RuntimeException("unable to initialize EGL14");
        }
// 试图获得GLES3 的context
        if( (flags and FLAG_TRY_GLES3)!= 0){
            getConfig(flags,3)?.let {
                val attrib3_list = intArrayOf(
                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
                    EGL14.EGL_NONE
                )

                val context = EGL14.eglCreateContext(
                    mEGLDisplay, it, sharedContext,
                    attrib3_list, 0
                )
                if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
                    //Log.d(TAG, "Got GLES 3 config");
                    mEGLConfig = it
                    mEGLContext = context
                    mGlVersion = 3
                }
            }

        }
        //只支持GLES 2.0
        if (mEGLContext==EGL14.EGL_NO_CONTEXT){
            var config: EGLConfig? =
                getConfig(flags,2) ?: throw RuntimeException("Unable to find a suitable EGLConfig");

            var attrib2_list=intArrayOf(
                EGL14.EGL_CONTEXT_CLIENT_VERSION,2,
                EGL14.EGL_NONE
            )

            var context=EGL14.eglCreateContext(mEGLDisplay,config,sharedContext,attrib2_list,0)
            mEGLConfig=config;
            mEGLContext=context;
            mGlVersion=2;
        }

        var values= intArrayOf(1)

        EGL14.eglQueryContext(mEGLDisplay,mEGLContext,EGL14.EGL_CONTEXT_CLIENT_VERSION,values,0)

    }
}